package com.itfreer.gis.server.tms;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.geotools.geometry.jts.ReferencedEnvelope;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.itfreer.gis.server.ProgressListener;
import com.itfreer.gis.server.mercator.MercatorProjection;
import com.itfreer.gis.server.mercator.TileUtils;
import com.itfreer.gis.server.storage.MinIOStorage;
import com.itfreer.gis.server.vector.FeatureDao;
import com.itfreer.gis.server.vector.generate.VectorTileGenerate;
import com.itfreer.gis.server.vector.mapbox.MapBoxTileBuilderFactory;

/**
 * TMS服务发布接口
 */
@Component
public class TMSServerImp implements TMSServer {

	@Autowired
	private FeatureDao featureDao;
	@Autowired
	private MinIOStorage minIOStorage;
	@Autowired
	private VectorTileGenerate vectorTileGenerate;
	// 切片构建工厂
	private MapBoxTileBuilderFactory factory = new MapBoxTileBuilderFactory();
	// 切片服务器的存储块
	private String tmsBucket = "tms";

	/**
	 * 多数据源切片处理
	 * 
	 * @param tmsName
	 * @param source
	 * @param extent
	 * @param progressListener
	 * @throws Exception
	 */
	public void generate(final String tmsName, final Map<Integer, String> source, double[] extent,
			final ProgressListener progressListener) throws Exception {
		// 排序
		Set<Integer> keys = source.keySet();
		List<Integer> keyList = new ArrayList<>();
		keyList.addAll(keys);
		keyList.sort(new Comparator<Integer>() {
			@Override
			public int compare(Integer o1, Integer o2) {
				return o1.compareTo(o2);
			}
		});
		for (Integer key : keyList) {
			String curSource = source.get(key);
			this.generate(tmsName, curSource, extent, key, key, progressListener);
		}
		progressListener.onSuccess();
	}

	/**
	 * 地图切片
	 *
	 * @param tmsName  服务名称
	 * @param source   数据源
	 * @param extent   切片范围，如：{ 117, 26, 123, 32 }
	 * @param minLevel 最小级别
	 * @param maxLevel 最大级别
	 * @throws Exception
	 */
	public void generate(final String tmsName, final String source, double[] extent, int minLevel, int maxLevel,
			final ProgressListener progressListener) throws Exception {
		vectorTileGenerate.setTileBuilderFactory(factory);
		// 1、读取数据源
		if (extent == null) {
			ReferencedEnvelope bounds = featureDao.getBounds(source);
			extent = new double[] { bounds.getMinX(), bounds.getMinY(), bounds.getMaxX(), bounds.getMaxY() };
		}
		final CoordinateReferenceSystem sourceCrs = featureDao.getCoordinateReferenceSystem(source);
		final String fields = featureDao.fields(source);
		final ThreadSharePara para = new ThreadSharePara();

		// 2.计算切片范围
		for (int z = minLevel; z <= maxLevel; z++) {

			// 起始结束列
			int minX = MercatorProjection.longitudeToTileX(extent[0], z);
			int maxX = MercatorProjection.longitudeToTileX(extent[2], z);

			// 起始结束行
			int minY = MercatorProjection.latitudeToTileY(extent[3], z);
			int maxY = MercatorProjection.latitudeToTileY(extent[1], z);

			int curSchedule = 0;
			int allSchedule = (maxX - minX + 1) * (maxY - minY + 1);
			progressListener.onChange(z, allSchedule, "");

			for (int x = minX; x <= maxX; x++) {
				for (int y = minY; y <= maxY; y++) {
					while (para.threadCount > 5) {
						//
					}
					para.threadCount++;
					final int xx = x;
					final int yy = y;
					final int zz = z;
					new Thread(new Runnable() {
						@Override
						public void run() {
							vector(tmsName, source, fields, sourceCrs, xx, yy, zz);
							para.threadCount--;
						}
					}).run();
					curSchedule++;
					progressListener.onSize(z, allSchedule, curSchedule, "");
				}
			}
		}

		progressListener.onSuccess();
	}

	class ThreadSharePara {
		int threadCount = 0;
	}

	/**
	 * 子线程切片
	 *
	 * @param tmsName
	 * @param source
	 * @param sourceCrs
	 * @param x
	 * @param y
	 * @param z
	 */
	private void vector(String tmsName, String source, String fields, CoordinateReferenceSystem sourceCrs, int x, int y,
			int z) {
		String key = tmsName + "/" + String.valueOf(z) + "/" + String.valueOf(x) + "/" + String.valueOf(y) + ".pbf";
		try {
			List<Map<String, Object>> features = queryFeature(source, fields, x, y, z);
			if (features == null || features.size() <= 0) {
				return;
			}
			byte[] pbf = vectorTileGenerate.generate(source, sourceCrs, features, x, y, z);
			if (pbf != null) {
				InputStream dfpStream = new ByteArrayInputStream(pbf);
				minIOStorage.putObject(tmsBucket, key, dfpStream, factory.getMimeType());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 查询数据
	 *
	 * @param source
	 * @param x
	 * @param y
	 * @param z
	 * @return
	 * @throws Exception
	 */
	private List<Map<String, Object>> queryFeature(String source, String fields, int x, int y, int z) throws Exception {
		String extent = TileUtils.parseXyz2Bound(x, y, z);
		List<Map<String, Object>> results = featureDao.queryFeature(source, fields, extent);
		return results;
	}
}
